﻿using System;
using System.Windows;
using System.Windows.Media;

namespace VisualView.Axis
{
    public class LogNumberAxis : BaseNumberAxis
    {
        /// <summary>
        /// 对数计算的底,必须是大于1的正数
        /// </summary>
        public double LogBase
        {
            get { return (double)GetValue(LogBaseProperty); }
            set { SetValue(LogBaseProperty, value); }
        }
        public static readonly DependencyProperty LogBaseProperty = DependencyProperty.Register("LogBase", typeof(double), typeof(LogNumberAxis), new PropertyMetadata(10d, RangeChangedNeedRefresh));

        /// <summary>
        /// 指数文字大小
        /// </summary>
        public double PowerFontSize
        {
            get { return (double)GetValue(PowerFontSizeProperty); }
            set { SetValue(PowerFontSizeProperty, value); }
        }
        public static readonly DependencyProperty PowerFontSizeProperty = DependencyProperty.Register("PowerFontSize", typeof(double), typeof(LogNumberAxis), new PropertyMetadata(8d, PropertyChangedNeedRefresh));

        public override void SetRange(double min, double max)
        {
            var tempmin = Math.Min(min, max);
            var tempmax = Math.Max(min, max);
            if (tempmax <= 0.01d) tempmax = 10d;
            base.SetRange(Math.Max(0.01d, tempmin), tempmax);
        }

        private double GetLogBase()
        {
            return LogBase > 1d ? LogBase : 10d;
        }

        public override object GetAxisValueConvertParam()
        {
            return GetLogBase();
        }

        public override AxisValueLocationConvertParam GetValueLocationParam()
        {
            var p = GetAxisValueConvertParam();
            var max = Range.Max;
            var min = Range.Min;
            if (min <= 0) min = 1d;
            if (max <= min) max = 100000d;
            return new NumberAxisValueLocationConvertParam(max, min, ValueToAxisValue(max, p), ValueToAxisValue(min, p), GetLength(), AxisType, p);
        }

        public override double ValueToAxisValue(double value, object param)
        {
            double logBase = 10d;
            if (param != null && param is double d && d > 1) logBase = d;
            if (double.IsNaN(value)) return value;
            else if (double.IsPositiveInfinity(value)) return double.MaxValue;
            else if (double.IsNegativeInfinity(value)) return double.MinValue;
            //else if (value <= 0) return double.MinValue;
            return Math.Log((value <= 0d ? 1d : value), logBase);
        }

        public override double AxisValueToValue(double value, object param)
        {
            double logBase = 10d;
            if (param != null && param is double d && d > 1) logBase = d;
            if (double.IsNaN(value)) return value;
            else if (double.IsPositiveInfinity(value)) return double.MaxValue;
            else if (double.IsNegativeInfinity(value)) return double.MinValue;
            return Math.Pow(logBase, value);
        }

        public override void Refresh()
        {
            var length = GetLength();
            if (length == 0d) return;
            var param = GetValueLocationParam() as NumberAxisValueLocationConvertParam;
            GetAxisStartAndPace(param, out double start, out double pace);
            var count = param.AxisValueDifference / pace;
            using (var dc = Visual.RenderOpen())
            {
                dc.DrawRectangle(Background, null, new Rect(0, 0, this.ActualWidth, this.ActualHeight));
                DrawingAxisLine(dc);
                for (int i = -1; i < count; i++)
                {
                    var axisValue = start + i * pace;
                    var location = GetAxisValueLocation(axisValue, param);
                    if (location >= 0 && location <= param.Length)
                    {
                        var logbasesg = CreateTextGeometry(LogBase == Math.E ? "e" : (LogBase == Math.PI ? "π" : LogBase.ToString()), FontSize, out double lbwidth, out double lbheight);
                        var sg = CreateTextGeometry(Convert.ToDecimal(axisValue).ToString(), PowerFontSize, out double width, out double height);
                        var allwidth = lbwidth + width;
                        if (param.AxisType == AxisType.X)
                        {
                            dc.DrawLine(LinePen, new Point(location, 0), new Point(location, 5));
                            sg.Transform = new MatrixTransform(1, 0, 0, 1, location + lbwidth / 2 - width / 2, 5);
                            logbasesg.Transform = new MatrixTransform(1, 0, 0, 1, location - allwidth / 2, 5);
                        }
                        else if (param.AxisType == AxisType.X2)
                        {
                            dc.DrawLine(LinePen, new Point(location, this.ActualHeight), new Point(location, this.ActualHeight - 5));
                            sg.Transform = new MatrixTransform(1, 0, 0, 1, location + lbwidth / 2 - width / 2, this.ActualHeight - lbheight - 5);
                            logbasesg.Transform = new MatrixTransform(1, 0, 0, 1, location - allwidth / 2, this.ActualHeight - lbheight - 5);
                        }
                        else if (param.AxisType == AxisType.Y)
                        {
                            dc.DrawLine(LinePen, new Point(this.ActualWidth, location), new Point(this.ActualWidth - 5, location));
                            sg.Transform = new MatrixTransform(0, -1, 1, 0, this.ActualWidth - 5 - lbheight, location - lbwidth / 2 + width / 2);
                            logbasesg.Transform = new MatrixTransform(0, -1, 1, 0, this.ActualWidth - 5 - lbheight, location + allwidth / 2);
                        }
                        else
                        {
                            dc.DrawLine(LinePen, new Point(0, location), new Point(5, location));
                            sg.Transform = new MatrixTransform(0, 1, -1, 0, 5 + lbheight, location + lbwidth / 2 - width / 2);
                            logbasesg.Transform = new MatrixTransform(0, 1, -1, 0, 5 + lbheight, location - allwidth / 2);
                        }
                        dc.DrawGeometry(TextPen.Brush, TextPen, logbasesg);
                        dc.DrawGeometry(TextPen.Brush, TextPen, sg);
                    }
                }
            }
        }
    }
}
